﻿using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

//http://absolutecobblers.blogspot.com/2008/06/how-to-move-viewstate-to-bottom-of-your.html
namespace HelpersDotNet.HttpModules
{
    public sealed class HttpViewstateMoverModule : IHttpModule
    {
        public void Dispose()
        {
            // nothing to dispose  
        }

        public void Init(HttpApplication context)
        {
            context.ReleaseRequestState += new EventHandler(context_ReleaseRequestState);
        }

        void context_ReleaseRequestState(object sender, EventArgs e)
        {
            HttpResponse response = HttpContext.Current.Response;
            // Uncomment the following line if you only want to recieve a single call to ViewstateMover.Write  
            // context.Response.Buffer = true;        
            if (response.ContentType == "text/html")
            {
                response.Filter = new ViewstateMover(response.Filter);
            }
        }
        private class ViewstateMover : Stream
        {
            // this class is private as it serves no real purpose outside this implementation      
            private Stream _output_stream;
            private long _position;
            private StringBuilder _output_buffer;

            /// <summary>        
            /// Creates a new instance of the ViewstateMover class  
            /// /// </summary>        
            /// <param name="input_stream">The HttpResponse.Filter to work with</param>  
            public ViewstateMover(Stream input_stream)
            {
                _output_stream = input_stream;
                _output_buffer = new StringBuilder();
            }

            #region Stream Members

            public override bool CanRead
            {
                get { return true; }
            }
            public override bool CanSeek { get { return true; } }
            public override bool CanWrite { get { return true; } }

            public override void Close()
            {
                _output_stream.Close();
            }
            public override void Flush()
            {
                _output_stream.Flush();
            }
            public override long Length { get { return 0; } }
            public override long Position
            {
                get { return _position; }
                set { _position = value; }
            }

            public override long Seek(long offset, SeekOrigin origin)
            {
                return _output_stream.Seek(offset, origin);
            }

            public override void SetLength(long length)
            {
                _output_stream.SetLength(length);
            }

            public override int Read(byte[] buffer, int offset, int count)
            {
                return _output_stream.Read(buffer, offset, count);
            }

            #endregion

            public override void Write(byte[] buffer, int offset, int count)
            {
                string strBuffer = UTF8Encoding.UTF8.GetString(buffer, offset, count);
                // check for the closing HTML tag           
                Regex eof = new Regex("</html>", RegexOptions.IgnoreCase);
                if (!eof.IsMatch(strBuffer))
                {
                    _output_buffer.Append(strBuffer);
                }
                else
                {
                    _output_buffer.Append(strBuffer);
                    string finalHtml = _output_buffer.ToString();
                    // original code from http://www.hanselman.com/blog/MovingViewStateToTheBottomOfThePage.aspx     
                    int StartPoint = finalHtml.IndexOf("<input type=\"hidden\" name=\"__VIEWSTATE\"");
                    if (StartPoint >= 0)
                    {
                        int EndPoint = finalHtml.IndexOf("/>", StartPoint) + 2;
                        string viewstateInput = finalHtml.Substring(StartPoint, EndPoint - StartPoint);
                        finalHtml = finalHtml.Remove(StartPoint, EndPoint - StartPoint);
                        int FormEndStart = finalHtml.IndexOf("</form>") - 1;
                        if (FormEndStart >= 0)
                        {
                            finalHtml = finalHtml.Insert(FormEndStart, viewstateInput);
                        }
                    }
                    byte[] data = UTF8Encoding.UTF8.GetBytes(finalHtml);
                    // write the page countents out to the user             
                    _output_stream.Write(data, 0, data.Length);
                }
            }
        }
    }
}